﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DotNet
{
    public static class Chainable
    {
        public static IQueryable<T> ChainableMethod<T>(this IQueryable<T> query)
        {
            return query;
        }

        public static IQueryable<T> ChainableMethod1<T>(IQueryable<T> query)
        {
            return query;
        }
    }

    public class ExtendClass
    {
        public void Program()
        {
            Car c = new Car("Mazda");
            c.Accelerate();
            // c.Break(); // Can't do this, because this Car does not know how to brake!

            // 1. Use Composition
            // Let's create a container object which has the Break() method and
            // feed it the original car object.
            CarContainer c1 = new CarContainer(c);
            c1.car.Accelerate();
            c1.Brake(); // this car knows how to break.

            // 2.
            // Let's create a child object that inerits Car's ability to accelerate
            // and adds functionality to break.
            ChildCar c2 = new ChildCar("Volvo");
            c2.Accelerate();
            c2.Brake(); // This car knows how to break too.

            // 3.
            // Let's create a third car, this is a normal car.
            // There has been defined an extension method to a Car object in the namespace,
            // so the BreakForHeavensSake() method is "glued" to this object.
            Car c3 = new Car("Mercedes-Benz");
            c3.Accelerate();
            c3.BrakeForHeavensSake(); // Works ok!

            
            var query = new List<int>().AsQueryable();
            query.ChainableMethod().Where(x => x == 1);
            Chainable.ChainableMethod1(query).Where(x => x == 1);
			var car = new CarExtender1().ExtendMethod();
            car.BrakeForHeavensSake(new Car());
            
        }
    }

    public class Car
    {
        public string Name { get; set; }
        public int Velocity { get; set; }

        public Car()
        {}

        public Car(string name)
        {
            this.Name = name;
            Console.WriteLine("\nCreated a {0}", name);
            Console.WriteLine("Velocity is {0}", this.Velocity);
        }

        public void Accelerate()
        {
            this.Velocity++;
            Console.WriteLine("Accelerated {0} to velocity {1}",
                this.Name, this.Velocity);
        }
    }

    // 1. 
    public class CarContainer
    {
        public Car car;

        public CarContainer(Car c)
        {
            this.car = c;
            Console.WriteLine("\nCreated container for {0}", this.car.Name);
        }

        public void Brake()
        {
            this.car.Velocity--;
            Console.WriteLine("Braked {0} to velocity {1} (with containment)",
                this.car.Name, this.car.Velocity);
        }
    }

    // 2.
    public class ChildCar : Car
    {
        public ChildCar(string name)
            : base(name)
        { }

        public void Brake()
        {
            this.Velocity--;
            Console.WriteLine("Braked {0} to velocity {1} (with child class)",
                this.Name, this.Velocity);
        }
    }

    // 3. 
    public static class CarExtender
    {
        public static void BrakeForHeavensSake(this Car c)
        {
            c.Velocity--;
            Console.WriteLine("Braked {0} to velocity {1} (with extension method)",
                c.Name, c.Velocity);
        }
    }
	
	// 4.
    public class CarExtender1
    {
        static CarExtender1()
        {

        }

        public void BrakeForHeavensSake(Car c)
        {
            c.Velocity--;
            Console.WriteLine("Braked {0} to velocity {1} (with extension method)",
                c.Name, c.Velocity);
        }

        public CarExtender1 ExtendMethod()
        {
            return this;
        }
    }
}
